﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using ESRI.ArcGIS.Geometry;
using ESRI.ArcGIS.Carto;
using ESRI.ArcGIS.Display;
using System.Windows.Forms;
using ESRI.ArcGIS.esriSystem;
using ESRI.ArcGIS.Output;
using System.IO;

namespace Akiha.Core
{
    public class MapExportClass
    {
        #region
        [DllImport("GDI32.dll")]
        public static extern int GetDeviceCaps(int hdc, int nIndex);

        [DllImport("User32.dll")]
        public static extern int GetDC(int hWnd);

        [DllImport("User32.dll")]
        public static extern int ReleaseDC(int hWnd, int hDC);

        [DllImport("user32.dll", SetLastError = true)]
        static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref int pvParam, uint fWinIni);

        /* constants used for user32 calls */
        const uint SPI_GETFONTSMOOTHING = 74;
        const uint SPI_SETFONTSMOOTHING = 75;
        const uint SPIF_UPDATEINIFILE = 0x1;
        #endregion

        /// <summary>
        /// 地图导出
        /// </summary>
        /// <param name="iOutputResolution">输出分辨率</param>
        /// <param name="lResampleRatio">输出质量(输出为矢量格式时用)，分为5个等级，1代表最佳，5代表较低</param>
        /// <param name="ExportType">输出类型</param>
        /// <param name="sOutputDir">输出目录</param>
        /// <param name="bClipToGraphicsExtent">是否裁剪到图形范围(一般打印视图时为true，否则为false)</param>
        public void MapExport(IActiveView docActiveView, double iOutputResolution, int lResampleRatio, string fileName, bool bClipToGraphicsExtent)
        {
            IExport docExport;
            long iPrevOutputImageQuality;
            IOutputRasterSettings docOutputRasterSettings;
            IEnvelope pixelBoundsEnv;
            tagRECT exportRECT;
            tagRECT displayBounds;
            IDisplayTransformation docDisplayTransformation;
            IPageLayout docPageLayout;
            IEnvelope docMapExtEnv;
            long hdc;
            long tmpDC;
            long iScreenResolution;
            bool bReenable = false;


            IEnvelope docGraphicsExtentEnv;
            IUnitConverter pUnitConvertor;
            if (docActiveView == null) return;
            if (GetFontSmoothing())
            {
                bReenable = true;
                DisableFontSmoothing();
                if (GetFontSmoothing())
                {
                    return;
                }
            }
            // The Export*Class() type initializes a new export class of the desired type.
            /*输出格式共10种，分为矢量格式和栅格格式。EMF、EPS、AI、PDF 和 SVG 称为矢量导出格式；
             * BMP、JPEG、PNG、TIFF 和 GIF 称为图像导出格式，属于栅格图形文件格式。
             */
            FileInfo fileInfo = new FileInfo(fileName);
            if (fileInfo.Extension == ".jpg")
            {
                docExport = new ExportJPEGClass();
            }
            else if (fileInfo.Extension == ".bmp")
            {
                docExport = new ExportBMPClass();
            }
            else if (fileInfo.Extension == ".gif")
            {
                docExport = new ExportGIFClass();
            }
            else if (fileInfo.Extension == ".png")
            {
                docExport = new ExportPNGClass();
            }
            else if (fileInfo.Extension == ".tif")
            {
                docExport = new ExportTIFFClass();
            }
            else if (fileInfo.Extension == ".pdf")
            {
                docExport = new ExportPDFClass();
            }
            else if (fileInfo.Extension == ".eps")
            {
                docExport = new ExportPSClass();
            }
            else if (fileInfo.Extension == ".ai")
            {
                docExport = new ExportAIClass();
            }
            else if (fileInfo.Extension == ".svg")
            {
                docExport = new ExportSVGClass();
            }
            else if (fileInfo.Extension == ".emf")
            {
                docExport = new ExportEMFClass();
            }
            else
            {
                docExport = new ExportJPEGClass();
            }
            docOutputRasterSettings = docActiveView.ScreenDisplay.DisplayTransformation as IOutputRasterSettings;
            iPrevOutputImageQuality = docOutputRasterSettings.ResampleRatio;


            if (docExport is IExportImage)
            {
                SetOutputQuality(docActiveView, 1);
            }
            else
            {
                SetOutputQuality(docActiveView, lResampleRatio);
            }
            docExport.ExportFileName = fileName;

            tmpDC = GetDC(0);
            iScreenResolution = GetDeviceCaps((int)tmpDC, 88); //88 is the win32 const for Logical pixels/inch in X)
            /* release the DC. */
            ReleaseDC(0, (int)tmpDC);
            docExport.Resolution = iOutputResolution;

            if (docActiveView is IPageLayout)
            {
                //get the bounds of the "exportframe" of the active view.
                displayBounds = docActiveView.ExportFrame;
                //set up pGraphicsExtent, used if clipping to graphics extent.
                docGraphicsExtentEnv = GetGraphicsExtent(docActiveView);
            }
            else
            {
                //Get the bounds of the deviceframe for the screen.
                docDisplayTransformation = docActiveView.ScreenDisplay.DisplayTransformation;
                displayBounds = docDisplayTransformation.get_DeviceFrame();
            }

            pixelBoundsEnv = new EnvelopeClass();

            if (bClipToGraphicsExtent && (docActiveView is IPageLayout))
            {
                docGraphicsExtentEnv = GetGraphicsExtent(docActiveView);
                docPageLayout = docActiveView as PageLayout;
                pUnitConvertor = new UnitConverterClass();

                //assign the x and y values representing the clipped area to the PixelBounds envelope
                pixelBoundsEnv.XMin = 0;
                pixelBoundsEnv.YMin = 0;
                pixelBoundsEnv.XMax = pUnitConvertor.ConvertUnits(docGraphicsExtentEnv.XMax, docPageLayout.Page.Units, esriUnits.esriInches) * docExport.Resolution - pUnitConvertor.ConvertUnits(docGraphicsExtentEnv.XMin, docPageLayout.Page.Units, esriUnits.esriInches) * docExport.Resolution;
                pixelBoundsEnv.YMax = pUnitConvertor.ConvertUnits(docGraphicsExtentEnv.YMax, docPageLayout.Page.Units, esriUnits.esriInches) * docExport.Resolution - pUnitConvertor.ConvertUnits(docGraphicsExtentEnv.YMin, docPageLayout.Page.Units, esriUnits.esriInches) * docExport.Resolution;

                //'assign the x and y values representing the clipped export extent to the exportRECT
                exportRECT.bottom = (int)(pixelBoundsEnv.YMax) + 1;
                exportRECT.left = (int)(pixelBoundsEnv.XMin);
                exportRECT.top = (int)(pixelBoundsEnv.YMin);
                exportRECT.right = (int)(pixelBoundsEnv.XMax) + 1;

                //since we're clipping to graphics extent, set the visible bounds.
                docMapExtEnv = docGraphicsExtentEnv;
            }
            else
            {
                double tempratio = iOutputResolution / iScreenResolution;
                double tempbottom = displayBounds.bottom * tempratio;
                double tempright = displayBounds.right * tempratio;
                exportRECT.bottom = (int)Math.Truncate(tempbottom);
                exportRECT.left = 0;
                exportRECT.top = 0;
                exportRECT.right = (int)Math.Truncate(tempright);
                pixelBoundsEnv.PutCoords(exportRECT.left, exportRECT.top, exportRECT.right, exportRECT.bottom);
                docMapExtEnv = null;
            }

            // Assign the envelope object to the exporter object's PixelBounds property.  The exporter object
            // will use these dimensions when allocating memory for the export file.
            docExport.PixelBounds = pixelBoundsEnv;

            // call the StartExporting method to tell docExport you're ready to start outputting.
            hdc = docExport.StartExporting();
            docActiveView.Output((int)hdc, (int)docExport.Resolution, ref exportRECT, docMapExtEnv, null);

            docExport.FinishExporting();
            docExport.Cleanup();

            SetOutputQuality(docActiveView, iPrevOutputImageQuality);
            if (bReenable)
            {
                /* reenable font smoothing if we disabled it before */
                EnableFontSmoothing();
                bReenable = false;
                if (!GetFontSmoothing())
                {
                    //error: cannot reenable font smoothing.
                    MessageBox.Show("Unable to reenable Font Smoothing", "Font Smoothing error");
                }
            }
            docMapExtEnv = null;
            pixelBoundsEnv = null;
        }

        private void SetOutputQuality(IActiveView docActiveView, long iResampleRatio)
        {
            /* This function sets OutputImageQuality for the active view.  If the active view is a pagelayout, then
             * it must also set the output image quality for EACH of the Maps in the pagelayout.
             */
            IGraphicsContainer oiqGraphicsContainer;
            IElement oiqElement;
            IOutputRasterSettings docOutputRasterSettings;
            IMapFrame docMapFrame;
            IActiveView TmpActiveView;

            if (docActiveView is IMap)
            {
                docOutputRasterSettings = docActiveView.ScreenDisplay.DisplayTransformation as IOutputRasterSettings;
                docOutputRasterSettings.ResampleRatio = (int)iResampleRatio;
            }
            else if (docActiveView is IPageLayout)
            {
                //assign ResampleRatio for PageLayout
                docOutputRasterSettings = docActiveView.ScreenDisplay.DisplayTransformation as IOutputRasterSettings;
                docOutputRasterSettings.ResampleRatio = (int)iResampleRatio;
                //and assign ResampleRatio to the Maps in the PageLayout
                oiqGraphicsContainer = docActiveView as IGraphicsContainer;
                oiqGraphicsContainer.Reset();

                oiqElement = oiqGraphicsContainer.Next();
                while (oiqElement != null)
                {
                    if (oiqElement is IMapFrame)
                    {
                        docMapFrame = oiqElement as IMapFrame;
                        TmpActiveView = docMapFrame.Map as IActiveView;
                        docOutputRasterSettings = TmpActiveView.ScreenDisplay.DisplayTransformation as IOutputRasterSettings;
                        docOutputRasterSettings.ResampleRatio = (int)iResampleRatio;
                    }
                    oiqElement = oiqGraphicsContainer.Next();
                }

                docMapFrame = null;
                oiqGraphicsContainer = null;
                TmpActiveView = null;
            }
            docOutputRasterSettings = null;
        }

        private IEnvelope GetGraphicsExtent(IActiveView docActiveView)
        {
            /* Gets the combined extent of all the objects in the map. */
            IEnvelope GraphicsBounds;
            IEnvelope GraphicsEnvelope;
            IGraphicsContainer oiqGraphicsContainer;
            IPageLayout docPageLayout;
            IDisplay GraphicsDisplay;
            IElement oiqElement;

            GraphicsBounds = new EnvelopeClass();
            GraphicsEnvelope = new EnvelopeClass();
            docPageLayout = docActiveView as IPageLayout;
            GraphicsDisplay = docActiveView.ScreenDisplay;
            oiqGraphicsContainer = docActiveView as IGraphicsContainer;
            oiqGraphicsContainer.Reset();

            oiqElement = oiqGraphicsContainer.Next();
            while (oiqElement != null)
            {
                oiqElement.QueryBounds(GraphicsDisplay, GraphicsEnvelope);
                GraphicsBounds.Union(GraphicsEnvelope);
                oiqElement = oiqGraphicsContainer.Next();
            }
            return GraphicsBounds;
        }

        private void DisableFontSmoothing()
        {
            bool iResult;
            int pv = 0;

            /* call to systemparametersinfo to set the font smoothing value */
            iResult = SystemParametersInfo(SPI_SETFONTSMOOTHING, 0, ref pv, SPIF_UPDATEINIFILE);
        }

        private void EnableFontSmoothing()
        {
            bool iResult;
            int pv = 0;

            /* call to systemparametersinfo to set the font smoothing value */
            iResult = SystemParametersInfo(SPI_SETFONTSMOOTHING, 1, ref pv, SPIF_UPDATEINIFILE);
        }

        private bool GetFontSmoothing()
        {
            bool iResult;
            int pv = 0;

            /* call to systemparametersinfo to get the font smoothing value */
            iResult = SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, ref pv, 0);

            if (pv > 0)
            {
                //pv > 0 means font smoothing is ON.
                return true;
            }
            else
            {
                //pv == 0 means font smoothing is OFF.
                return false;
            }
        }
    }
}
